# coding: utf-8

# -------------------------------------------------------------------------------
# Name:         ext_method_runner.py
# Description:  扩展方法执行器
# Author:       XiangjunZhao
# EMAIL:        2419352654@qq.com
# Date:         2020/12/10 15:38
# -------------------------------------------------------------------------------
import builtins
import json
import logging
import time
import types

from django.utils import timezone

from apps.HttpAutoTestService.core import ext_methods
from apps.HttpAutoTestService.core.ext_methods import *
from apps.HttpAutoTestService.core.http_client import HttpSession
from apps.HttpAutoTestService.core.http_session_context import HttpSessionContext
from apps.HttpAutoTestService.core.loader import load_ext_method_online
from apps.HttpAutoTestService.core.utils import regex_parse_args, parse_output_result, parse_ext_method_validate_result
from apps.HttpAutoTestService.models import ExtMethodResult

logger = logging.getLogger(__name__)


class ExtMethodRunner(object):

    def __init__(self, ext_method_online_module=None, ext_methods_online=None):
        """

        Args:
            ext_method_online_module：ext_method_online.py模块
            ext_methods_online：ext_method_online.py模块中的方法
        """
        # 扩展函数
        self.ext_methods = {}
        # 系统内置函数
        self.builtins = {}
        self.ext_method_online_module = ext_method_online_module
        # 在线扩展方法(优先级高于 扩展函数和系统内置函数)
        self.ext_methods_online = ext_methods_online
        for name, item in vars(ext_methods).items():
            if isinstance(item, types.FunctionType) or name in ext_class:
                self.ext_methods[name] = item

        for name, item in vars(builtins).items():
            if isinstance(item, types.BuiltinFunctionType):
                self.builtins[name] = item

        # 动态加载ext_method_online.py模块中内容
        if ext_method_online_module is None:
            self.ext_method_online_module, self.ext_methods_online = load_ext_method_online()

    def run(self, http_session: HttpSession = None, http_session_context: HttpSessionContext = None,
            ext_method_name=None, ext_method=None, expect_result=None, executor=None, is_periodictask=True,
            loop_count=1):
        """
        执行扩展方法
        Args:
            http_session: HttpSession实例
            http_session_context:HttpSessionContext实例
            ext_method_name: 需要执行的扩展方法名称
            ext_method: 需要执行的扩展方法
            expect_result: 期望结果
            executor: 执行者
            is_periodictask: 是否是定时任务
            loop_count: 循环次数

        Returns:

        """
        variables_mapping = {**http_session_context.session_variables_mapping,
                             **http_session_context.output_variables_mapping}
        ext_method_result_list = []
        method_name = ext_method.split('(')[0]
        ext_method_result_mapping = {
            "ext_method_name": ext_method_name or method_name,
        }
        # 如果是定时任务，就不需要保存执行人信息，保存数据时，数据库有默认值；如果是非定时任务，就保存实际执行人信息
        if not is_periodictask:
            ext_method_result_mapping.update({
                'executor_id': executor.id,
                'executor_real_name': executor.real_name,
                'is_periodictask': is_periodictask
            })
        try:
            ext_method = regex_parse_args(content=ext_method, variables_mapping=variables_mapping,
                                          ext_method_online_module=self.ext_method_online_module,
                                          ext_methods_online=self.ext_methods_online)
        except Exception as e:
            ext_method_result_mapping.update({
                "ext_method": ext_method,
                "ext_method_run_result": "",
                "execute_time": timezone.now(),
                "elapsed_ms": 0,
                "status": "FAIL",
                "failure_reason": f"解析扩展方法【{ext_method}】异常，异常原因：{repr(e)}",
                "context_global_variable": variables_mapping
            })
            ext_method_result = ExtMethodResult(**ext_method_result_mapping)
            ext_method_result_list.append(ext_method_result)
            return ext_method_result_list

        logger.info(f'执行 {ext_method} 扩展方法')

        if method_name in self.ext_methods or method_name in self.builtins or method_name in self.ext_methods_online:
            # 期望结果
            expect_result = regex_parse_args(content=expect_result, variables_mapping=variables_mapping,
                                             ext_method_online_module=self.ext_method_online_module,
                                             ext_methods_online=self.ext_methods_online)
            expect_result = json.loads(expect_result) if expect_result else {}
            logger.info('期望结果：{expect_result}'.format(expect_result=expect_result))
            # 循环执行设定次数
            for i in range(loop_count):
                # 执行时间
                execute_time = timezone.now()
                # 开始时间，毫秒
                start_ms = time.time() * 1000
                try:
                    if method_name in self.ext_methods_online:
                        # 执行在线扩展方法(优先级高于 扩展函数和系统内置函数)
                        result = eval(f'self.ext_method_online_module.{ext_method}')
                    else:
                        # 扩展函数或系统内置函数
                        result = eval(ext_method)
                except Exception as e:
                    result = repr(e)
                finally:
                    # 结束时间，毫秒
                    end_ms = time.time() * 1000
                    elapsed_ms = end_ms - start_ms
                logger.info(f'执行结果：{result}')

                # 存储提取输出的变量值
                output_result = parse_output_result(name=ext_method,
                                                    result={'results': result},
                                                    expect_result=expect_result,
                                                    http_session_context=http_session_context)
                # 校验扩展方法执行结果
                validate_pass, failure_reason = parse_ext_method_validate_result(
                    result={'results': result}, expect_result=expect_result, http_session_context=http_session_context,
                    ext_method_online_module=self.ext_method_online_module, ext_methods_online=self.ext_methods_online
                )

                ext_method_result_mapping.update({
                    "ext_method": ext_method,
                    "ext_method_run_result": result,
                    "execute_time": execute_time,
                    "elapsed_ms": elapsed_ms,
                    "status": validate_pass,
                    'output_result': output_result if output_result else "",
                    "failure_reason": failure_reason if validate_pass == "FAIL" else "",
                    "context_global_variable": variables_mapping
                })

                if validate_pass == 'FAIL':
                    logger.warning('扩展方法：{method_name}，测试未通过；原因：{failure_reason}'.format(method_name=method_name,
                                                                                         failure_reason=failure_reason))
                ext_method_result = ExtMethodResult(**ext_method_result_mapping)
                ext_method_result_list.append(ext_method_result)
        return ext_method_result_list

    def debug(self, http_session: HttpSession = None, http_session_context: HttpSessionContext = None,
              ext_method_name=None, ext_method=None, expect_result=None):
        """
        调试扩展方法
        Args:
            http_session: HttpSession实例
            http_session_context: HttpSessionContext实例
            ext_method_name: 需要执行的扩展方法名称
            ext_method: 需要执行的扩展方法
            expect_result: 期望结果

        Returns:

        """
        variables_mapping = {**http_session_context.session_variables_mapping,
                             **http_session_context.output_variables_mapping}
        ext_method = regex_parse_args(content=ext_method, variables_mapping=variables_mapping,
                                      ext_method_online_module=self.ext_method_online_module,
                                      ext_methods_online=self.ext_methods_online)
        logger.info(f'执行 {ext_method} 扩展方法')

        method_name = ext_method.split('(')[0]
        ext_method_result = {
            "ext_method_name": ext_method_name or method_name,
            "ext_method": ext_method
        }

        if method_name in self.ext_methods or method_name in self.builtins or method_name in self.ext_methods_online:
            # 期望结果
            expect_result = regex_parse_args(content=expect_result, variables_mapping=variables_mapping,
                                             ext_method_online_module=self.ext_method_online_module,
                                             ext_methods_online=self.ext_methods_online)
            expect_result = json.loads(expect_result) if expect_result else {}
            logger.info('期望结果：{expect_result}'.format(expect_result=expect_result))
            # 执行时间
            execute_time = timezone.now()
            # 开始时间，毫秒
            start_ms = time.time() * 1000
            try:
                if method_name in self.ext_methods_online:
                    # 执行在线扩展方法(权重高于 扩展函数和系统内置函数)
                    result = eval(f'self.ext_method_online_module.{ext_method}')
                else:
                    # 扩展函数或系统内置函数
                    result = eval(ext_method)
            except Exception as e:
                result = repr(e)
            finally:
                # 结束时间，毫秒
                end_ms = time.time() * 1000
                elapsed_ms = end_ms - start_ms
            logger.info(f'执行结果：{result}')

            # 存储提取输出的变量值
            output_result = parse_output_result(name=ext_method,
                                                result={'results': result},
                                                expect_result=expect_result,
                                                http_session_context=http_session_context)
            # 校验扩展方法执行结果
            validate_pass, failure_reason = parse_ext_method_validate_result(
                result={'results': result}, expect_result=expect_result, http_session_context=http_session_context,
                ext_method_online_module=self.ext_method_online_module, ext_methods_online=self.ext_methods_online
            )

            ext_method_result.update({
                "execute_time": execute_time,
                "elapsed_ms": elapsed_ms,
                "ext_method_run_result": result,
                "status": validate_pass,
                "output_result": output_result if output_result else "",
                "failure_reason": failure_reason if validate_pass == "FAIL" else "",
            })

            if validate_pass == 'FAIL':
                logger.warning(f'扩展方法：{method_name}，调试未通过；原因：{failure_reason}')
        return ext_method_result
